﻿using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Media;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using Path = System.IO.Path;

namespace FunCam
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        #region Overlays (INotifyPropertyChanged Property)
        private ObservableCollection<string> _overlays;

        public ObservableCollection<string> Overlays
        {
            get { return _overlays; }
            set
            {
                _overlays = value;
                RaisePropertyChanged("Overlays");
            }
        }
        #endregion

        #region Effects (INotifyPropertyChanged Property)
        private ObservableCollection<EffectEntry> _effects;

        public ObservableCollection<EffectEntry> Effects
        {
            get { return _effects; }
            set
            {
                _effects = value;
                RaisePropertyChanged("Effects");
            }
        }
        #endregion

        #region IsAutoEnabled (INotifyPropertyChanged Property)
        private bool _isAutoEnabled;

        public bool IsAutoEnabled
        {
            get { return _isAutoEnabled; }
            set
            {
                _isAutoEnabled = value;
                RaisePropertyChanged("IsAutoEnabled");
            }
        }
        #endregion
        
        private DispatcherTimer timer;
        private SoundPlayer player;

        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;

            player = new SoundPlayer(Properties.Resources.snap);
            player.Load();
            
            timer = new DispatcherTimer();
            timer.Tick += new EventHandler(timer_Tick);

            Overlays = new ObservableCollection<string>();
            Overlays.Add("images/borders/ivy.png");
            Overlays.Add("images/borders/bamboo.png");
            Overlays.Add("images/borders/border3.png");
            Overlays.Add("images/borders/rough.png");
            Overlays.Add("images/overlays/mark.png");
            Overlays.Add("images/borders/port_turq_border.png");
            Overlays.Add("images/borders/creepy.png");
            Overlays.Add("images/borders/vine.png");
            Overlays.Add("images/borders/RoughBorder2.png");
            Overlays.Add("images/borders/stars.png");
            
            Effects = new ObservableCollection<EffectEntry>();
            Effects.Add(new EffectEntry { Name = "Banded Swirl", ClassName = "Shazzam.Shaders.BandedSwirlEffect" });
            Effects.Add(new EffectEntry { Name = "Bands", ClassName = "Shazzam.Shaders.BandsEffect" });
            Effects.Add(new EffectEntry { Name = "Bright Extract", ClassName = "Shazzam.Shaders.BrightExtractEffect" });
            Effects.Add(new EffectEntry { Name = "Color Tone", ClassName = "Shazzam.Shaders.ColorToneEffect" });
            Effects.Add(new EffectEntry { Name = "Contrast Adjust", ClassName = "Shazzam.Shaders.ContrastAdjustEffect" });
            Effects.Add(new EffectEntry { Name = "Directional Blur", ClassName = "Shazzam.Shaders.DirectionalBlurEffect" });
            Effects.Add(new EffectEntry { Name = "Embossed", ClassName = "Shazzam.Shaders.EmbossedEffect" });
            Effects.Add(new EffectEntry { Name = "Frosty Outline", ClassName = "Shazzam.Shaders.FrostyOutlineEffect" });
            Effects.Add(new EffectEntry { Name = "Glass Tile", ClassName = "Shazzam.Shaders.GlassTileEffect" });
            Effects.Add(new EffectEntry { Name = "Gloom", ClassName = "Shazzam.Shaders.GloomEffect" });
            Effects.Add(new EffectEntry { Name = "Invert Color", ClassName = "Shazzam.Shaders.InvertColorEffect" });
            Effects.Add(new EffectEntry { Name = "Magnify", ClassName = "Shazzam.Shaders.MagnifyEffect" });
            Effects.Add(new EffectEntry { Name = "Monochrome", ClassName = "Shazzam.Shaders.MonochromeEffect" });
            Effects.Add(new EffectEntry { Name = "Old Movie", ClassName = "Shazzam.Shaders.OldMovie" });
            Effects.Add(new EffectEntry { Name = "Parametric Edge Detection", ClassName = "Shazzam.Shaders.ParametricEdgeDetection" });
            Effects.Add(new EffectEntry { Name = "Pinch", ClassName = "Shazzam.Shaders.PinchEffect" });
            Effects.Add(new EffectEntry { Name = "Pixelate", ClassName = "Shazzam.Shaders.PixelateEffect" });
            Effects.Add(new EffectEntry { Name = "Ripple", ClassName = "Shazzam.Shaders.RippleEffect" });
            Effects.Add(new EffectEntry { Name = "Sharpen", ClassName = "Shazzam.Shaders.SharpenEffect" });
            Effects.Add(new EffectEntry { Name = "Sketch - Granite", ClassName = "Shazzam.Shaders.SketchGraniteEffect" });
            Effects.Add(new EffectEntry { Name = "Sketch - Pencil Stroke", ClassName = "Shazzam.Shaders.SketchPencilStrokeEffect" });
            Effects.Add(new EffectEntry { Name = "Smooth Magnify", ClassName = "Shazzam.Shaders.SmoothMagnifyEffect" });
            Effects.Add(new EffectEntry { Name = "Swirl", ClassName = "Shazzam.Shaders.SwirlEffect" });
            Effects.Add(new EffectEntry { Name = "Tiler", ClassName = "Shazzam.Shaders.Tiler" });
            Effects.Add(new EffectEntry { Name = "Toon", ClassName = "Shazzam.Shaders.ToonShaderEffect" });
            Effects.Add(new EffectEntry { Name = "Wave Warper", ClassName = "Shazzam.Shaders.WaveWarperEffect" });
            Effects.Add(new EffectEntry { Name = "Zoom Blur", ClassName = "Shazzam.Shaders.ZoomBlurEffect" });

            PlayBtn.Visibility = System.Windows.Visibility.Collapsed;
            PauseBtn.Visibility = System.Windows.Visibility.Collapsed;

            videoElement.MediaFailed += new EventHandler<WPFMediaKit.DirectShow.MediaPlayers.MediaFailedEventArgs>(videoElement_MediaFailed);
            videoElement.MediaOpened += new RoutedEventHandler(videoElement_MediaOpened);
            videoElement.MediaClosed += new RoutedEventHandler(videoElement_MediaClosed);

            IsAutoEnabled = false;
            this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
        }

        void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            var s = Properties.Settings.Default;
            if (string.IsNullOrEmpty(s.SaveLocation))
            {
                try
                {
                    s.SaveLocation = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures),
                                                  "FunCam");
                    Directory.CreateDirectory(s.SaveLocation);
                    s.Save();
                }
                catch( Exception x)
                {
                    MessageBox.Show(
                        "Sorry, I had trouble creating a folder to save photos.  You can use the Settings button to set one yourself.");
                }
            }

            if (!DesignerProperties.GetIsInDesignMode(videoElement))
            {
                if (videoCapDevices.Items.Count > 0)
                    videoCapDevices.SelectedIndex = 0;
            }
        }

        private void PlayCamera()
        {
            if( Properties.Settings.Default.PlayShutterSound)
                player.Play();
        }

        void videoElement_MediaClosed(object sender, RoutedEventArgs e)
        {
            PlayBtn.Visibility = Visibility.Visible;
            PauseBtn.Visibility = Visibility.Collapsed;
        }

        void videoElement_MediaOpened(object sender, RoutedEventArgs e)
        {
            PlayBtn.Visibility = Visibility.Collapsed;
            PauseBtn.Visibility = Visibility.Visible;
        }

        void videoElement_MediaFailed(object sender, WPFMediaKit.DirectShow.MediaPlayers.MediaFailedEventArgs e)
        {
            PlayBtn.Visibility = Visibility.Visible;
            PauseBtn.Visibility = Visibility.Collapsed;

            MessageBox.Show("I'm sorry, but I couldn't lock your webcam.  Maybe another application is using it already.");
        }

        // Take control and use it as a brush to render into a differently-sized region
        private RenderTargetBitmap CaptureBitmap(Visual target, int width, int height)
        {
            if (target == null) return null;

            Rect bounds = VisualTreeHelper.GetDescendantBounds(target);
            var rtb = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
            var dv = new DrawingVisual();
            using (DrawingContext ctx = dv.RenderOpen())
            {
                var vb = new VisualBrush(target);
                ctx.DrawRectangle(vb, null, new Rect(0, 0, width, height));
            }
            rtb.Render(dv);
            return rtb;
        }

        private void SaveImage()
        {
            try
            {
                string fn = GetNextFilename();
                RenderTargetBitmap bmp = CaptureBitmap(VideoHost, videoElement.DesiredPixelWidth,
                                                       videoElement.DesiredPixelHeight);

                string ext = Path.GetExtension(fn).ToLower();
                BitmapEncoder encoder = null;

                switch (ext)
                {
                    case ".jpg":
                        encoder = new JpegBitmapEncoder();
                        break;
                    case ".png":
                        encoder = new PngBitmapEncoder();
                        break;
                    case ".bmp":
                        encoder = new BmpBitmapEncoder();
                        break;
                    case ".gif":
                        encoder = new GifBitmapEncoder();
                        break;
                    case ".tif":
                        encoder = new TiffBitmapEncoder();
                        break;
                }

                if (encoder == null) return;
                encoder.Frames.Add(BitmapFrame.Create(bmp));

                using (Stream stm = File.Create(fn))
                {
                    encoder.Save(stm);
                }
                PlayCamera();
            }
            catch(Exception x)
            {
                MessageBox.Show("Sorry, I had trouble saving that photo.\n\nError: " + x.Message);
                IsAutoEnabled = false;
                timer.Stop();
            }
        }

        #region INotifyPropertyChanged values

        public event PropertyChangedEventHandler PropertyChanged;

        protected void RaisePropertyChanged(string propertyName)
        {
            var handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        #endregion

        private void SaveBtn_Click(object sender, RoutedEventArgs e)
        {
            SaveImage();
        }

        private string GetNextFilename()
        {
            int num = 0;
            var s = Properties.Settings.Default;
            string fn = Path.Combine(s.SaveLocation, s.SaveBasename + num + "." + s.SaveFormat);

            while (File.Exists(fn))
            {
                num++;
                fn = Path.Combine(s.SaveLocation, s.SaveBasename + num + "." + s.SaveFormat);
            }
            return fn;
        }

        private void PlayBtn_Click(object sender, RoutedEventArgs e)
        {
            videoElement.Play();

            PlayBtn.Visibility = Visibility.Collapsed;
            PauseBtn.Visibility = Visibility.Visible;
        }

        private void PauseBtn_Click(object sender, RoutedEventArgs e)
        {
            videoElement.Stop();

            PlayBtn.Visibility = Visibility.Visible;
            PauseBtn.Visibility = Visibility.Collapsed;
        }

        private void OverlayList_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            OverlaysBtn.IsChecked = true;
        }

        private void OverlaysBtn_Checked(object sender, RoutedEventArgs e)
        {
            if (OverlaysBtn.IsChecked.HasValue && OverlaysBtn.IsChecked.Value)
                OverlayImg.Visibility = Visibility.Visible;
            else
                OverlayImg.Visibility = Visibility.Collapsed;
        }

        private void EffectsList_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            EffectsBtn.IsChecked = true;
        }

        private void EffectsBtn_Checked(object sender, RoutedEventArgs e)
        {
            if (!EffectsBtn.IsChecked.HasValue || !EffectsBtn.IsChecked.Value)
                EffectsList.SelectedIndex = -1;
        }

        private void SettingsBtn_Click(object sender, RoutedEventArgs e)
        {
            var win = new SettingsWindow();
            win.ShowDialog();
        }

        private void btnTimer_Click(object sender, RoutedEventArgs e)
        {
            if (btnTimer.IsChecked.HasValue && btnTimer.IsChecked.Value)
            {
                IsAutoEnabled = true;
                timer.Interval = TimeSpan.FromMinutes(Properties.Settings.Default.AutoSaveInterval);
                timer.Start();
            }
            else
            {
                timer.Stop();
                IsAutoEnabled = false;
            }
        }

        void timer_Tick(object sender, EventArgs e)
        {
            if (videoElement.IsPlaying) SaveImage();
        }
    }
}
